//
// Created by LiuYou on 2021/9/5.
//

#include <iostream>
#include "DbConnectionPool.hpp"

DbConnectionPool::DbConnectionPool() {
    this->CurConn = 0;
    this->FreeConn = 0;
}

DbConnectionPool::~DbConnectionPool() {
    destroyPool();
}

//DbConnectionPool::DbConnectionPool(const std::string& url, const std::string& user, const std::string& passWord,
//                                   const std::string& databaseName, int port, unsigned int maxConn) :
//        MaxConn( maxConn ), url( url ), Port( port ),
//        User( user ), PassWord( passWord ),
//        DatabaseName( databaseName ) {
//    lock.lock();
//    for (int i = 0; i < MaxConn; i++) {
//        MYSQL* con = nullptr;
//        con = mysql_init( con );
//
//        if (con == nullptr) {
//            std::cout << "Error:" << mysql_error( con );
//            exit( 1 );
//        }
////        con = mysql_real_connect( con, url.c_str(), User.c_str(), PassWord.c_str(), databaseName.c_str(),
////                                  atoi( Port.c_str()), nullptr,
////                                  0 );
//        con = mysql_real_connect( con, url.c_str(), User.c_str(), PassWord.c_str(), databaseName.c_str(),
//                                  Port, nullptr,
//                                  0 );
//
//        if (con == nullptr) {
//            std::cout << "Error: " << mysql_error( con ) << std::endl;
//            exit( 1 );
//        }
//        connList.push_back( con );
//        ++FreeConn;
//    }
//
//    reserve = Semaphore( FreeConn );
//
//    this->MaxConn = FreeConn;
//
//    lock.unlock();
//}

void DbConnectionPool::init(const std::string& url, const std::string& user, const std::string& passWord,
                            const std::string& databaseName,
                            int port, unsigned int maxConn) {
//    DbConnectionPool( url, user, passWord, databaseName, port, maxConn );
    lock.lock();
    for (int i = 0; i < MaxConn; i++) {
        MYSQL* con = nullptr;
        con = mysql_init( con );

        if (con == nullptr) {
            std::cout << "Error:" << mysql_error( con );
            exit( 1 );
        }
//        con = mysql_real_connect( con, url.c_str(), User.c_str(), PassWord.c_str(), databaseName.c_str(),
//                                  atoi( Port.c_str()), nullptr,
//                                  0 );
        con = mysql_real_connect( con, url.c_str(), User.c_str(), PassWord.c_str(), databaseName.c_str(),
                                  Port, nullptr,
                                  0 );

        if (con == nullptr) {
            std::cout << "Error: " << mysql_error( con ) << std::endl;
            exit( 1 );
        }
        connList.push_back( con );
        ++FreeConn;
    }

    reserve = Semaphore( FreeConn );

    this->MaxConn = FreeConn;

    lock.unlock();
}

DbConnectionPool* DbConnectionPool::getInstance() {
    static DbConnectionPool connPool;
    return &connPool;
}

MYSQL* DbConnectionPool::getConnection() {
    MYSQL* con = NULL;

    if (0 == connList.size())
        return NULL;

    reserve.wait();

    lock.lock();

    con = connList.front();
    connList.pop_front();

    --FreeConn;
    ++CurConn;

    lock.unlock();
    return con;
}

bool DbConnectionPool::releaseConnection(MYSQL* conn) {
    if (NULL == conn)
        return false;

    lock.lock();

    connList.push_back( conn );
    ++FreeConn;
    --CurConn;

    lock.unlock();

    reserve.post();
    return true;
}

int DbConnectionPool::getFreeConn() {
    return this->FreeConn;
}

void DbConnectionPool::destroyPool() {
    lock.lock();
    if (connList.size() > 0) {
        std::list<MYSQL*>::iterator it;
        for (it = connList.begin(); it != connList.end(); ++it) {
            MYSQL* con = *it;
            mysql_close( con );
        }
        CurConn = 0;
        FreeConn = 0;
        connList.clear();

        lock.unlock();
    }

    lock.unlock();
}

DbConnectionRAII::DbConnectionRAII(MYSQL** conRaii, DbConnectionPool* poolRaii) :
        poolRAII( poolRaii ) {
    *conRaii = poolRaii->getConnection();
    this->conRAII = *conRaii;
}

DbConnectionRAII::~DbConnectionRAII() {
    poolRAII->releaseConnection( conRAII );
}
